/*
* Copyright (C) 2010, Jared Jennings <jaredljennings@gmail.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.IO;
using System.Xml;
using System.Collections.Generic;
using Gtk;
namespace org.jaredjennings.mysshmenu
{
	public class StoreBackend
	{
		private String xml_file_name = "servers.xml";
		private List<HostObj> hosts = new List<HostObj>();
		private String conf_location;
		private static StoreBackend instance;
		Gtk.ListStore musicListStore = new Gtk.ListStore (typeof(HostObj));

		public StoreBackend ()
		{
			conf_location = Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData) + "/mysshmenu/";
			createConfigDirLocation();
			Logger.Debug ("Config location loaded and set to {0}", conf_location);
			populateFromXml();
		}
		public static StoreBackend GetInstance()
		{
			if(instance == null)
			{
				instance = new StoreBackend();
			}

			return instance;
		}		
		
		public HostObj getHost( HostObj host)
		{
			return hosts[getHostLocInList(host)];
		}
		
		/// <summary>
		/// Returns a liststore object containing the configured hosts.
		/// </summary>
		/// <returns>
		/// A <see cref="Gtk.ListStore"/>
		/// </returns>
		public Gtk.ListStore getListValues ()
		{
			return musicListStore;
		}
		
		/// <summary>
		/// Updates and existing host entry which is already stored in the system.
		/// </summary>
		/// <param name="host">
		/// A <see cref="HostObj"/>
		/// </param>
		public void update(HostObj host) {
			Logger.Debug ("Update()" + host.NAME);
			int idx = getHostLocInList(host);
			if(idx<0) {
				Logger.Debug (	"Adding host " + host.NAME);
			    hosts.Add(host);
			} else {
				Logger.Debug ("updating host in list " + host.NAME + " - " + host.ADDRESS);
			    hosts[idx] = host;
			}
			reloadListStore();
		}
		
		/// <summary>
		/// Removes specified host from configuration
		/// </summary>
		/// <param name="host">
		/// A <see cref="HostObj"/>
		/// </param>
		public void delete(HostObj host) {
			Logger.Debug ("Delete() host from model " + host.NAME);
			int idx = getHostLocInList(host);
			
			if(idx != -1) {
				Logger.Debug (	"Found host and deleting from list " + host.NAME);
			    hosts.RemoveAt(idx);
			} else {
				Logger.Debug ("HOST NOT FOUND IN LIST " + host.NAME + " - " + host.ADDRESS);
			}
			reloadListStore();		
		}
		
		/// <summary>
		/// Gets the index value of a specified host from the List of hosts.
		/// </summary>
		/// <param name="host">
		/// A <see cref="HostObj"/>
		/// </param>
		/// <returns>
		/// A <see cref="System.Int32"/>
		/// </returns>
		public int getHostLocInList(HostObj host) {
			int index = hosts.FindIndex(
			    delegate(HostObj hostObj)
			    {
			        return hostObj.NAME.Equals(host.NAME, StringComparison.Ordinal);
			    });	
			Logger.Debug ("host found at index {0}", index);
			return index;
		}
		
		/// <summary>
		/// Add a new remote host connection.
		/// </summary>
		/// <param name="name">
		/// A <see cref="String"/>
		/// </param>
		/// <param name="address">
		/// A <see cref="String"/>
		/// </param>
		/// <param name="port">
		/// A <see cref="String"/>
		/// </param>
		/// <param name="conntype">
		/// A <see cref="String"/>
		/// </param>
		public void addTo(HostObj host) {			
			Logger.Debug ("Saving to XML  Name:{0} - Address:{1}", host.NAME, host.ADDRESS);
			//TODO: Need to determine if the port is empty and some other values that are default and don't always have to be specified.
			hosts.Add(host);
			reloadListStore();	
		}
		
		/// <summary>
		/// Stores current List<> of hosts to the storage location, XML or whatever.
		/// Reloads the liststore from the storage location
		/// </summary>
		private void reloadListStore() {
			saveToXml();
			populateFromXml();
		}

		void saveToXml() {
			
			XmlWriterSettings settings = new XmlWriterSettings();
			settings.Indent = true;
			settings.IndentChars = ("    ");

			using (XmlWriter writer = XmlWriter.Create(conf_location + xml_file_name, settings))
			{
			    writer.WriteStartDocument();
			    writer.WriteStartElement("servers");
						
				foreach (HostObj hostobj in hosts) {
				    // Write XML data.
				    writer.WriteStartElement("server");
					
				    writer.WriteElementString("name", hostobj.NAME);
					writer.WriteElementString("address", hostobj.ADDRESS);
					writer.WriteElementString("port", hostobj.PORT);
					writer.WriteElementString("conntype", hostobj.CONNTYPE.ToString());
				    
					writer.WriteEndElement();
				    writer.Flush();
				}
			    writer.WriteEndElement();
			    writer.WriteEndDocument();
					
			}

			
		}
		
		protected void populateFromXml ()
		{
			musicListStore.Clear();
			XmlDocument doc = new XmlDocument ();
			try {
				doc.Load (conf_location + xml_file_name);
				XmlElement root = doc.DocumentElement;
				XmlNodeList nodes = root.SelectNodes("//servers");
			
				foreach (XmlNode node in nodes)
				{
					foreach (XmlNode n in node)
					{
						HostObj hobj = new HostObj();
						string name = n["name"].InnerText;
						string address = n["address"].InnerText;
						string port = n["port"].InnerText;
						Logger.Debug ("Loading servers from XML " + name + " - " + address);
						hobj.NAME = name;
						hobj.ADDRESS = address;
						hobj.PORT = port;
						hosts.Add(hobj);
						musicListStore.AppendValues (hobj);
					}
				}
			}
			catch (System.IO.FileNotFoundException e) {
				Logger.Debug ("exception", e);
			}
		}
		
		/// <summary>
		/// Creates the specified folder location so that the servers.xml can be created and written to.
		/// </summary>
		protected void createConfigDirLocation() {
			        
			if (!System.IO.File.Exists(conf_location))
        	{
	            System.IO.Directory.CreateDirectory(conf_location);
				Logger.Debug ("location created {0}", conf_location);
			}

		}
	}
}

